/*
    $Id$
    MAR another horizontal scroll action game
    Copyright (C) 2009 Japan Manoretimedia Lab.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "stage.h"
#include "util.h"
#include <stdio.h>
#include <windows.h>
#include "stdint.h" //< not included in vc2008
#include <assert.h>

struct Stage {
    StageElementType *stageElements;
    int      stageWidth;
    int      stageHeight;
};

Stage *
Stage_Instance(void)
{
    static Stage self;

    return &self;
}

bool
Stage_Init(Stage *self)
{
    assert(self);

    self->stageElements = NULL;
    return true;
}

void
Stage_Term(Stage *self)
{
    assert(self);

    SAFE_DELETE_ARRAY(self->stageElements);
}

static StageElementType
RgbToStageElementType(uint32_t rgb)
{
    StageElementType se = SE_None;
    switch (rgb) {
    case 0x6f3198: // purple
        se = SE_Player;
        break;
    case 0x9c5a3c: // brown
        se = SE_Brick;
        break;
    case 0x000000: // black
        se = SE_Goal;
        break;
    }
    return se;
}

static bool
ReadBmp(Stage *self, FILE *fp)
{
    BITMAPFILEHEADER bmpfh;
    BITMAPINFOHEADER bmpih;
    if (14 != fread(&bmpfh, 1, 14, fp)) {
        return false;
    }
    if (40 != fread(&bmpih, 1, 40, fp)) {
        return false;
    }
    if (24 != bmpih.biBitCount ||
        BI_RGB != bmpih.biCompression ||
        bmpih.biHeight < 0 ||
        (bmpih.biWidth & 3) != 0) {
        return false;
    }
    uint32_t bgrDataSize = bmpih.biWidth * bmpih.biHeight * 3;
    uint8_t *bgrData = new uint8_t[bgrDataSize];
    if (bgrDataSize != fread(bgrData, 1, bgrDataSize, fp)) {
        SAFE_DELETE_ARRAY(bgrData);
        return false;
    }

    self->stageWidth  = bmpih.biWidth;
    self->stageHeight = bmpih.biHeight;
    self->stageElements = new StageElementType[bmpih.biWidth * bmpih.biHeight];
    for (int h=0; h<bmpih.biHeight; ++h) {
        for (int w=0; w<bmpih.biWidth; ++w) {
            uint32_t pos = 3 * (w + h*bmpih.biWidth);
            uint32_t rgb =
                bgrData[pos] + (bgrData[pos+1] <<8) + (bgrData[pos+2]<<16);
            self->stageElements[w + h*bmpih.biWidth] =
                RgbToStageElementType(rgb);
        }
    }

    SAFE_DELETE_ARRAY(bgrData);
    return true;
}

bool
Stage_Load(Stage *self, const char *stageFile)
{
    assert(self);

    SAFE_DELETE_ARRAY(self->stageElements);

    FILE *fp = NULL;
    errno_t er = fopen_s(&fp, stageFile, "rb");
    if (0 != er || NULL == fp) {
        char path[FILE_PATH_NUM];
        strcpy_s(path, sizeof path -1, "../");
        strcat_s(path, sizeof path -1, stageFile);
        er = fopen_s(&fp, path, "rb");
        if (0 != er || NULL == fp) {
            DebugMessageBox(L"E: could not load %S", stageFile);
            return false;
        }
    }

    bool bRv = ReadBmp(self, fp);
    if (!bRv) {
        DebugMessageBox(L"E: read error %S", stageFile);
    }

    fclose(fp);
    return bRv;
}

int
Stage_GetStageWidth(Stage *self)
{
    assert(self);
    return self->stageWidth;
}

int
Stage_GetStageHeight(Stage *self)
{
    assert(self);
    return self->stageHeight;
}

StageElementType
Stage_GetStageElement(Stage *self, int x, int y)
{
    assert(self);
    if (x < 0 || self->stageWidth <= x ||
        y < 0 || self->stageHeight <= y) {
        return SE_None;
    }

    return self->stageElements[x + y*self->stageWidth];
}
